﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Cron
{
    public static class CronExtension
    {
        /// <summary>
        /// 当前时间,与设定时间比较.
        /// 结果相同返回真
        /// </summary>
        /// <param name="now">当前时间</param>
        /// <param name="minutes">分</param>
        /// <param name="hours">小时</param>
        /// <param name="days">日</param>
        /// <param name="months">有</param>       
        /// <param name="week">星期</param>
        /// <returns>true</returns>
        public static bool CompareDateTime(DateTime now, string minutes, string hours, string days, string months, string week)
        {
            bool m, h, d, M, w;
            m = h = d = M = w = false;

            //分钟处理,包含/的需要每多少分钟执行一次,同时包含-的是在多少分钟到多内,多少分钟执行一次
            //如 12-20/1 为从12钟钟开始到20钟结束,每分钟执行一次
            m = StrDecompose(now.Minute, minutes);

            //小时处理
            h = StrDecompose(now.Hour, hours);

            //按天处理
            if (days == "L")
            {
                //处理每月的最后天
                DateTime d1 = new DateTime(now.Year, now.Month, 1);
                DateTime d2 = d1.AddMonths(1).AddDays(-1);
                if (now.Day == d2.Day) d = true;
            }
            else
            {
                d = StrDecompose(now.Day, days);
            }

            //按月
            M = StrDecompose(now.Month, months);


            //按周                  
            w = StrDecompose(WeekToInt(now.DayOfWeek), week);

            //return m & h & M & (d | w);
            return m & h & M & d & w;
        }


        /// <summary>
        /// 数字与表达式比较
        /// 数字在字串表达式中可以找到值,则返回真.如果字为*号,则直接返回真.
        /// </summary>
        /// <param name="q">需要比对的数字</param>
        /// <param name="s">字中表达式</param>
        /// <returns>true</returns>
        private static bool StrDecompose(int q, string s)
        {
            Boolean result = false;

            string[] s1;
            if (s == "*")
            {
                result = true;
            }
            else if (s.IndexOf('/') >= 0)
            {
                s1 = s.Split('/');
                if (s1[0].IndexOf('-') >= 0 || s1[0].IndexOf(',') >= 0)
                {
                    if (decoder(s1[0].ToString()).IndexOf(q.ToString()) >= 0)
                        if (q % Convert.ToInt32(s1[1]) == 0) result = true;
                }
                else
                {
                    if (q % Convert.ToInt32(s1[1]) == 0) result = true;
                }
            }
            else
            {
                if (s.IndexOf('-') >= 0 || s.IndexOf(',') >= 0)
                {
                    if (decoder(s).IndexOf(q.ToString()) >= 0) result = true;
                }
                else
                {
                    if (q == Convert.ToInt32(s)) result = true;
                }
            }


            return result;

        }

        /// <summary>
        /// 解码表达式为字串列表
        /// </summary>
        /// <param name="s">表达式</param>
        /// <returns></returns>
        private static List<string> decoder(string s)
        {
            List<string> s1 = new List<string>();
            List<string> s2 = new List<string>();
            List<string> ret = new List<string>();
            s1 = s.Split(',').ToList();
            foreach (var s3 in s1)
            {
                s2 = s3.Split('-').ToList();
                if (s2.Count > 1)
                {
                    for (int i = Convert.ToInt32(s2[0]); i <= Convert.ToInt32(s2[1]); i++)
                    {
                        ret.Add(i.ToString());
                    }
                }
                else
                {
                    ret.Add(s2[0].ToString());
                }

            }
            s1 = null;
            s2 = null;
            return ret;
        }

        /// <summary>
        /// 星期转换成数字
        /// </summary>
        /// <param name="dayOfWeek"></param>
        /// <returns>数字</returns>
        private static int WeekToInt(DayOfWeek dayOfWeek)
        {
            switch (dayOfWeek)
            {
                case DayOfWeek.Sunday:
                    return 0;
                case DayOfWeek.Monday:
                    return 1;
                case DayOfWeek.Tuesday:
                    return 2;
                case DayOfWeek.Wednesday:
                    return 3;
                case DayOfWeek.Thursday:
                    return 4;
                case DayOfWeek.Friday:
                    return 5;
                case DayOfWeek.Saturday:
                    return 6;
                default:
                    return -1;
            }
        }

    }
}